#include "EnumBuilder.h"
#include "ScriptCompiler/AST/EnumNode.h"
#include "ScriptCompiler/ScriptFileContext.h"
#include "ScriptCompiler/ASTBuilder/EnumValueBuilder.h"

CEnumBuilder::CEnumBuilder(std::shared_ptr<CScriptFileContext> InContext)
{
    Context = InContext;
    Result = std::make_shared<CEnumNode>(Context);
}

bool CEnumBuilder::ReadCurentToken()
{
    //check and skip enum keyword
    {
        if (Context->GetCurrentToken()->Type != EScriptTokenType::Enum)
        {
            Context->RaiseError(10038,Context->GetCurrentToken(), U"Expected 'enum' keyword");
            return false;
        }
        Result->Enum = Context->GetCurrentToken();
        Context->CurrentTokenIndex++;
    }

    //build name
    {
        auto Token = Context->GetCurrentToken();
        if (Token->Type == EScriptTokenType::Identifier)
        {
            Result->Name = Token;
            Context->CurrentTokenIndex++;
        }
        else
        {
            Context->RaiseError(10039,Token, U"Expected enum name");
            return false;
        }
    }

    //skip {
    {
        auto Token = Context->GetCurrentToken();
        if (Token->Type == EScriptTokenType::LeftBrace)
        {
            Context->CurrentTokenIndex++; //skip '{'
        }
        else
        {
            Context->RaiseError(10040,Token, U"Expected '{' after enum name");
            return false;
        }
    }

    //build values
    {
        auto RightBraceIndexOptional = FindPairedBrace(Context->CurrentTokenIndex,Context);

        if(!RightBraceIndexOptional.HasValue())
        {
            Context->RaiseError(100180,Context->GetCurrentToken(), U"Expected '}' after enum values");
            return false;
        }
        int64_t RightBraceIndex = RightBraceIndexOptional.GetValue();
        int64_t CurrentIndex=Context->CurrentTokenIndex;
        while (Context->CurrentTokenIndex<RightBraceIndex)
        {
            //skip {} () []
            if(Context->GetCurrentToken()->Type == EScriptTokenType::LeftBrace)
            {
                auto RightBraceIndex = FindPairedBrace(CurrentIndex,Context);
                
                if(!RightBraceIndex.HasValue())
                {
                    Context->RaiseError(100181,Context->GetCurrentToken(), U"Expected '}' after enum values");
                    return false;
                }
                
                CurrentIndex= RightBraceIndex.GetValue() + 1;
                continue;
            }

            if(Context->GetCurrentToken()->Type == EScriptTokenType::LeftBracket)
            {
                auto RightBracketIndex = FindPairedBracket(CurrentIndex,Context);
                
                if(!RightBracketIndex.HasValue())
                {
                    Context->RaiseError(100182,Context->GetCurrentToken(), U"Expected ']' after enum values");
                    return false;
                }
                
                CurrentIndex = RightBracketIndex.GetValue() + 1;
                continue;
            }

            if(Context->GetCurrentToken()->Type == EScriptTokenType::LeftParentheses)
            {
                auto RightParenthesesIndex = FindPairedParentheses(CurrentIndex,Context);
                
                if(!RightParenthesesIndex.HasValue())
                {
                    Context->RaiseError(100183,Context->GetCurrentToken(), U"Expected ')' after enum values");
                    return false;
                }

                CurrentIndex = RightParenthesesIndex.GetValue() + 1;
                continue;
            }

            
            int64_t ValueEndTokenIndex = RightBraceIndex;
            bool bEndWithComma = false;
            //find ,
            {
                auto CommaIndex =FindNextToken(
                    {EScriptTokenType::Comma }
                    ,Context
                    ,CurrentIndex
                    ,RightBraceIndex
                    );
                if(CommaIndex.HasValue())
                {
                    ValueEndTokenIndex = CommaIndex.GetValue();
                    bEndWithComma = true;
                }
            }

            //build value
            auto EnumValueBuilder = std::make_shared<CEnumValueBuilder>(Context,ValueEndTokenIndex);
            if (!EnumValueBuilder->ReadCurentToken())
            {
                return false;
            }
            Result->Values.Add(EnumValueBuilder->Result);

            assert(Context->CurrentTokenIndex == ValueEndTokenIndex);

            //skip ,
            if(bEndWithComma)
            {
                Context->CurrentTokenIndex++; //skip ','
                CurrentIndex = Context->CurrentTokenIndex;
            }
        }

        //skip }
        {
            auto Token = Context->GetCurrentToken();
            if (Token->Type == EScriptTokenType::RightBrace)
            {
                Context->CurrentTokenIndex++; //skip '}'
            }
            else
            {
                Context->RaiseError(10041,Token, U"Expected '}' after enum values");
                return false;
            }
        }
    }

    return true;
}
